﻿//**********************************************************
//Copyright(C)2010 By 波特商网公司 版权所有。
//
//文件名：
//文件功能：ComboBox自定义控件
//
//创建标识：齐李平 || 2011-04-28
//
//修改标识：
//修改描述：
//**********************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace Hmc.IM.Common.UserControls
{
    /// <summary>
    /// ComboBox自定义控件
    /// </summary>
    [ToolboxBitmap(typeof(ComboBox))]
    public class ucComboBox : ComboBox, IValidate
    {
        #region Fields

        private IntPtr _editHandle;
        private ControlState _buttonState;
        private Color _baseColor = Color.FromArgb(219, 224, 230);
        private Color _borderColor = Color.FromArgb(219, 224, 230);
        //下拉箭头颜色
        private Color _arrowColor = Color.FromArgb(19, 88, 128);
        private bool _bPainting;


        /// <summary>
        /// 错误消息提醒
        /// </summary>
        ucErrorProvider errorInfo = new ucErrorProvider();

        #endregion

        #region Constructors

        /// <summary>
        /// 
        /// </summary>
        public ucComboBox()
            : base()
        {
            this.ForeColor = Color.FromArgb(115, 115, 115);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.DisplayMember = "Text";
            this.ValueMember = "Value";
        }

        #endregion

        #region Properties
        /// <summary>
        /// 
        /// </summary>
        [DefaultValue(typeof(Color), "219, 224, 230")]
        public Color BaseColor
        {
            get { return _baseColor; }
            set
            {
                if (_baseColor != value)
                {
                    _baseColor = value;
                    base.Invalidate();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [DefaultValue(typeof(Color), "219, 224, 230")]
        public Color BorderColor
        {
            get { return _borderColor; }
            set
            {
                if (_borderColor != value)
                {
                    _borderColor = value;
                    base.Invalidate();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [DefaultValue(typeof(Color), "19, 88, 128")]
        public Color ArrowColor
        {
            get { return _arrowColor; }
            set
            {
                if (_arrowColor != value)
                {
                    _arrowColor = value;
                    base.Invalidate();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        internal ControlState ButtonState
        {
            get { return _buttonState; }
            set
            {
                if (_buttonState != value)
                {
                    _buttonState = value;
                    Invalidate(ButtonRect);
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        internal Rectangle ButtonRect
        {
            get
            {
                return GetDropDownButtonRect();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        internal bool ButtonPressed
        {
            get
            {
                if (IsHandleCreated)
                {
                    return GetComboBoxButtonPressed();
                }
                return false;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        internal IntPtr EditHandle
        {
            get { return _editHandle; }
        }
        /// <summary>
        /// 
        /// </summary>
        internal Rectangle EditRect
        {
            get
            {
                if (DropDownStyle == ComboBoxStyle.DropDownList)
                {
                    Rectangle rect = new Rectangle(
                        3, 3, Width - ButtonRect.Width - 6, Height - 6);
                    if (RightToLeft == RightToLeft.Yes)
                    {
                        rect.X += ButtonRect.Right;
                    }
                    return rect;
                }
                if (IsHandleCreated && EditHandle != IntPtr.Zero)
                {
                    NativeMethods.RECT rcClient = new NativeMethods.RECT();
                    NativeMethods.GetWindowRect(EditHandle, ref rcClient);
                    return RectangleToClient(rcClient.Rect);
                }
                return Rectangle.Empty;
            }
        }

        #endregion

        #region Override Methods
        /// <summary>
        /// 
        /// </summary>
        protected override void OnCreateControl()
        {
            base.OnCreateControl();

            NativeMethods.ComboBoxInfo cbi = GetComboBoxInfo();
            _editHandle = cbi.hwndEdit;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            Point point = e.Location;
            if (ButtonRect.Contains(point))
            {
                ButtonState = ControlState.Hover;
            }
            else
            {
                ButtonState = ControlState.Normal;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);

            Point point = PointToClient(Cursor.Position);
            if (ButtonRect.Contains(point))
            {
                ButtonState = ControlState.Hover;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);

            ButtonState = ControlState.Normal;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            ButtonState = ControlState.Normal;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case NativeMethods.WM_PAINT:
                    WmPaint(ref m);
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        #endregion

        #region Windows Message Methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="m"></param>
        private void WmPaint(ref Message m)
        {
            if (base.DropDownStyle == ComboBoxStyle.Simple)
            {
                base.WndProc(ref m);
                return;
            }

            if (base.DropDownStyle == ComboBoxStyle.DropDown)
            {
                if (!_bPainting)
                {
                    NativeMethods.PAINTSTRUCT ps =
                        new NativeMethods.PAINTSTRUCT();

                    _bPainting = true;
                    NativeMethods.BeginPaint(m.HWnd, ref ps);

                    RenderComboBox(ref m);

                    NativeMethods.EndPaint(m.HWnd, ref ps);
                    _bPainting = false;
                    m.Result = NativeMethods.TRUE;
                }
                else
                {
                    base.WndProc(ref m);
                }
            }
            else
            {
                base.WndProc(ref m);
                RenderComboBox(ref m);

                #region 控件样式为DropdownList时选中文本改变颜色效果 陈博生 2011年5月17日
                if (m.Msg == 0x000F || m.Msg == 0x133)
                {
                    IntPtr hDC = NativeMethods.GetWindowDC(m.HWnd);
                    if (hDC.ToInt32() == 0)
                    {
                        return;
                    }

                    //建立Graphics对像
                    Graphics g = Graphics.FromHdc(hDC);

                    using (SolidBrush b = new SolidBrush(this.ForeColor))
                    {

                        g.FillRectangle(new SolidBrush(this.BackColor),
                            new Rectangle(1, 1, Width - Height , Height - 2));
                        g.DrawString(this.Text, Font, b, 1, 3, StringFormat.GenericDefault);
                    }
                    g.Dispose();
                    NativeMethods.ReleaseDC(m.HWnd, hDC);
                }
                #endregion

            }
        }

        #endregion

        #region Render Methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="m"></param>
        private void RenderComboBox(ref Message m)
        {
            Rectangle rect = new Rectangle(Point.Empty, Size);
            Rectangle buttonRect = ButtonRect;
            ControlState state = ButtonPressed ?
                ControlState.Pressed : ButtonState;
            using (Graphics g = Graphics.FromHwnd(m.HWnd))
            {
                RenderComboBoxBackground(g, rect, buttonRect);
                RenderConboBoxDropDownButton(g, ButtonRect, state);
                RenderConboBoxBorder(g, rect);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="g"></param>
        /// <param name="rect"></param>
        private void RenderConboBoxBorder(
            Graphics g, Rectangle rect)
        {
            Color borderColor = base.Enabled ?
                _borderColor : SystemColors.ControlDarkDark;
            using (Pen pen = new Pen(borderColor))
            {
                rect.Width--;
                rect.Height--;
                g.DrawRectangle(pen, rect);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="g"></param>
        /// <param name="rect"></param>
        /// <param name="buttonRect"></param>
        private void RenderComboBoxBackground(
            Graphics g,
            Rectangle rect,
            Rectangle buttonRect)
        {
            Color backColor = base.Enabled ?
                base.BackColor : SystemColors.Control;
            using (SolidBrush brush = new SolidBrush(backColor))
            {
                buttonRect.Inflate(-1, -1);
                rect.Inflate(-1, -1);
                using (Region region = new Region(rect))
                {
                    region.Exclude(buttonRect);
                    region.Exclude(EditRect);
                    g.FillRegion(brush, region);
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="g"></param>
        /// <param name="buttonRect"></param>
        /// <param name="state"></param>
        private void RenderConboBoxDropDownButton(
            Graphics g,
            Rectangle buttonRect,
            ControlState state)
        {
            Color baseColor;
            Color backColor = Color.FromArgb(160, 250, 250, 250);
            Color borderColor = base.Enabled ?
                _borderColor : SystemColors.ControlDarkDark;
            Color arrowColor = base.Enabled ?
                _arrowColor : SystemColors.ControlDarkDark;
            Rectangle rect = buttonRect;

            if (base.Enabled)
            {
                switch (state)
                {
                    case ControlState.Hover:
                        baseColor = RenderHelper.GetColor(
                            _baseColor, 0, -33, -22, -13);
                        break;
                    case ControlState.Pressed:
                        baseColor = RenderHelper.GetColor(
                            _baseColor, 0, -65, -47, -25);
                        break;
                    default:
                        baseColor = _baseColor;
                        break;
                }
            }
            else
            {
                baseColor = SystemColors.ControlDark;
            }

            rect.Inflate(-1, -1);

            RenderScrollBarArrowInternal(
                g,
                rect,
                baseColor,
                borderColor,
                backColor,
                arrowColor,
                RoundStyle.None,
                true,
                false,
                ArrowDirection.Down,
                LinearGradientMode.Vertical);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="g"></param>
        /// <param name="rect"></param>
        /// <param name="baseColor"></param>
        /// <param name="borderColor"></param>
        /// <param name="innerBorderColor"></param>
        /// <param name="arrowColor"></param>
        /// <param name="roundStyle"></param>
        /// <param name="drawBorder"></param>
        /// <param name="drawGlass"></param>
        /// <param name="arrowDirection"></param>
        /// <param name="mode"></param>
        internal void RenderScrollBarArrowInternal(
           Graphics g,
           Rectangle rect,
           Color baseColor,
           Color borderColor,
           Color innerBorderColor,
           Color arrowColor,
           RoundStyle roundStyle,
           bool drawBorder,
           bool drawGlass,
           ArrowDirection arrowDirection,
           LinearGradientMode mode)
        {
            RenderHelper.RenderBackgroundInternal(
               g,
               rect,
               baseColor,
               borderColor,
               innerBorderColor,
               roundStyle,
               0,
               .45F,
               drawBorder,
               drawGlass,
               mode);

            using (SolidBrush brush = new SolidBrush(arrowColor))
            {
                RenderArrowInternal(
                    g,
                    rect,
                    arrowDirection,
                    brush);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="g"></param>
        /// <param name="dropDownRect"></param>
        /// <param name="direction"></param>
        /// <param name="brush"></param>
        internal void RenderArrowInternal(
            Graphics g,
            Rectangle dropDownRect,
            ArrowDirection direction,
            Brush brush)
        {
            Point point = new Point(
                dropDownRect.Left + (dropDownRect.Width / 2),
                dropDownRect.Top + (dropDownRect.Height / 2));
            Point[] points = null;
            switch (direction)
            {
                case ArrowDirection.Left:
                    points = new Point[] { 
                        new Point(point.X + 2, point.Y - 3), 
                        new Point(point.X + 2, point.Y + 3), 
                        new Point(point.X - 1, point.Y) };
                    break;

                case ArrowDirection.Up:
                    points = new Point[] { 
                        new Point(point.X - 3, point.Y + 2), 
                        new Point(point.X + 3, point.Y + 2), 
                        new Point(point.X, point.Y - 2) };
                    break;

                case ArrowDirection.Right:
                    points = new Point[] {
                        new Point(point.X - 2, point.Y - 3), 
                        new Point(point.X - 2, point.Y + 3), 
                        new Point(point.X + 1, point.Y) };
                    break;

                default:
                    points = new Point[] {
                        new Point(point.X - 2, point.Y - 1), 
                        new Point(point.X + 3, point.Y - 1), 
                        new Point(point.X, point.Y + 2) };
                    break;
            }
            g.FillPolygon(brush, points);
        }

        #endregion

        #region Help Methods
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private NativeMethods.ComboBoxInfo GetComboBoxInfo()
        {
            NativeMethods.ComboBoxInfo cbi = new NativeMethods.ComboBoxInfo();
            cbi.cbSize = Marshal.SizeOf(cbi);
            NativeMethods.GetComboBoxInfo(base.Handle, ref cbi);
            return cbi;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private bool GetComboBoxButtonPressed()
        {
            NativeMethods.ComboBoxInfo cbi = GetComboBoxInfo();
            return cbi.stateButton ==
                NativeMethods.ComboBoxButtonState.STATE_SYSTEM_PRESSED;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private Rectangle GetDropDownButtonRect()
        {
            NativeMethods.ComboBoxInfo cbi = GetComboBoxInfo();

            return cbi.rcButton.Rect;
        }

        #endregion

        #region 实现IUserControl接口方法

        private bool isAllowEmpty = true;

        /// <summary>
        /// 设置是否允许为空
        /// </summary>
        [Browsable(true), Category("验证"), DefaultValue(true), Description("设置控件的值是否允许为空")]
        public bool IsAllowEmpty
        {
            get { return isAllowEmpty; }
            set { isAllowEmpty = value; }
        }

        /// <summary>
        /// 验证
        /// </summary>
        /// <returns></returns>
        public bool Validate()
        {
            errorInfo.Clear();
            if (!this.IsAllowEmpty)
            {
                if ((this.SelectedIndex == -1) || (this.SelectedValue == null) 
                    || (this.SelectedValue.ToString() == string.Empty) || (this.SelectedValue.ToString() == "-1")
                    || (this.SelectedValue.ToString() == Guid.Empty.ToString()))
                {
                    errorInfo.SetError(this, "该项不能为空！");
                    return this.IsValidated = false;
                }
            }
            if ((this.Expression != null) && (this.Expression.Trim() != string.Empty)
                && (!Regex.IsMatch(this.Text,this.Expression)))
            {
                errorInfo.SetError(this, this.SetError);
                return this.IsValidated = false;
            }
            return this.IsValidated = true;
        }

        /// <summary>
        /// 验证是否通过
        /// </summary>
        [Browsable(false), Category("验证"), Description("获取该控件是否已验证通过")]
        public bool IsValidated
        {
            get;
            set;
        }

        /// <summary>
        /// 设置与内容匹配的正则表达式
        /// </summary>
        [Browsable(true), Category("验证"), Description("设置要匹配的正则表达式")]
        public string Expression
        {
            get;
            set;
        }

        /// <summary>
        /// 设置验证不通过的错误提示信息
        /// </summary>
        [Browsable(true), Category("验证"), Description("设置验证未通过时提示的内容")]
        public string SetError
        {
            get;
            set;
        }

        #endregion

        /// <summary>
        /// 重写验证事件
        /// </summary>
        /// <param name="e"></param>
        protected override void OnValidating(CancelEventArgs e)
        {
            Validate();
            base.OnValidating(e);
        }
        
    }
}
